package com.redis.distributed.Utils;

import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.util.Random;
import java.util.concurrent.TimeUnit;
@Slf4j
public class RedisLock {
    private StringRedisTemplate redisTemplate;

    /**
     * 默认请求锁的超时时间(ms 毫秒)
     */
    private static final long TIME_OUT = 100;

    /**
     * 默认锁的有效时间(s)
     */
    public static final int EXPIRE = 60;

    /**
     * 锁标志对应的key
     */
    private String lockKey;
    /**
     * 锁的有效时间(s)
     */
    private int expireTime = EXPIRE;

    /**
     * 请求锁的超时时间(ms)
     */
    private long timeOut = TIME_OUT;

    /**
     * 锁标记
     */
    private volatile boolean locked = false;

    final Random random = new Random();

    /**
     * 使用默认的锁过期时间和请求锁的超时时间
     *
     * @param redisTemplate
     * @param lockKey       锁的key（Redis的Key）
     */
    public RedisLock(StringRedisTemplate redisTemplate, String lockKey) {
        this.redisTemplate = redisTemplate;
        this.lockKey = lockKey + "_lock";
    }

    /**
     * 使用默认的锁的过期时间，指定请求锁的超时时间
     *
     * @param redisTemplate
     * @param lockKey       锁的key（Redis的Key）
     * @param timeOut       请求锁的超时时间(单位：毫秒)
     */
    public RedisLock(StringRedisTemplate redisTemplate, String lockKey, long timeOut) {
        this(redisTemplate, lockKey);
        this.timeOut = timeOut;
    }

    public boolean lock() {
        // 请求锁超时时间，纳秒
        long timeout = timeOut * 1000000;
        log.info("timeOut "+timeOut);
        // 系统当前时间，纳秒
        long nowTime = System.nanoTime();
        while ((System.nanoTime() - nowTime) < timeout) {
            if(redisTemplate.opsForValue().setIfAbsent(lockKey,"true", expireTime,TimeUnit.SECONDS)){
                locked = true;
                return true;
            }

            try {
                Thread.sleep(10, random.nextInt(50000));
                log.info("sleep");
            } catch (InterruptedException e) {
                log.error("sleep",e);
            }

        }
        return false;
    }

    /**
     * 解锁
     */
    public void unlock() {
        log.info("unlock " +locked);
        if (locked) {
//            log.info("unlock");
            redisTemplate.delete(lockKey);
            locked = false;
        }
    }
}
